//////////////////////////////////////////ok
#include"stdafx.h"
#include "bochs.h"

void  IA32_CPU::load_seg_reg( ia32_segment_seg_t *seg, Bit16u new_value)
{
	ia32_descriptor_t descriptor;
	Bit32u		dword2[2] = {};
	Bit16u		index = new_value >> 3;
	Bit8u		ti = (new_value >> 2) & 0x01;
	Bit8u		rpl = (new_value & 0x03);

	if (seg == & sregs[IA32_SEG_REG_SS])
	{
		if ((new_value & 0xfffc) == 0) 
		{ 
			/* null selector */
			exception(IA32_GP_EXCEPTION, 0, 0);
			return;
		}

		m_vm32->access_xdt( this, (ti==0), IA32_READ, index, dword2 );

		//////////////////////////////////////////////////////////////////////////
		if (rpl != CPL) 
		{
			exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
			return;
		}

		parse_descriptor( dword2[0], dword2[1], &descriptor);

		if (descriptor.valid==0) 
		{
			exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
			return;
		}

		if ( (descriptor.segment==0) || descriptor.u.segment.executable || descriptor.u.segment.r_w==0 )
		{
			exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
		}

		if (descriptor.dpl != CPL) 
		{
			exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
		}

		if (descriptor.p == 0) 
		{
			exception(IA32_SS_EXCEPTION, new_value & 0xfffc, 0);
		}

		sregs[IA32_SEG_REG_SS].selector.value = new_value;
		sregs[IA32_SEG_REG_SS].selector.index = index;
		sregs[IA32_SEG_REG_SS].selector.ti    = ti;
		sregs[IA32_SEG_REG_SS].selector.rpl   = rpl;
		sregs[IA32_SEG_REG_SS].cache          = descriptor;
		sregs[IA32_SEG_REG_SS].cache.valid    = 1;
	}
	else 
	{
		if( (seg ==& sregs[IA32_SEG_REG_DS]) || 
			(seg ==& sregs[IA32_SEG_REG_ES]) || 
			(seg ==& sregs[IA32_SEG_REG_FS]) ||
			(seg ==& sregs[IA32_SEG_REG_GS]) )
		{
			if ((new_value & 0xfffc) == 0) 
			{ 
				seg->selector.index = 0;
				seg->selector.ti = 0;
				seg->selector.rpl = 0;
				seg->selector.value = 0;
				seg->cache.valid = 0; /* invalidate null selector */
				return;
			}

			m_vm32->access_xdt( this, (ti==0), IA32_READ, index, dword2 );

			parse_descriptor( dword2[0], dword2[1], &descriptor);

			if (descriptor.valid==0) 
			{
				exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
				return;
			}

			if ( descriptor.segment==0 ||(descriptor.u.segment.executable==1 && descriptor.u.segment.r_w==0) )
			{
				exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
				return;
			}

			if ( descriptor.u.segment.executable==0 ||  descriptor.u.segment.c_ed==0 )
			{
				if ((rpl > descriptor.dpl) || (CPL > descriptor.dpl)) 
				{
					exception(IA32_GP_EXCEPTION, new_value & 0xfffc, 0);
					return;
				}
			}

			if (descriptor.p == 0) 
			{
				exception(IA32_NP_EXCEPTION, new_value & 0xfffc, 0);
				return;
			}

			seg->selector.value  = new_value;
			seg->selector.index  = index;
			seg->selector.ti     = ti;
			seg->selector.rpl    = rpl;
			seg->cache           = descriptor;
			seg->cache.valid     = 1;
		}

		if (!(dword2[1] & 0x0100)) 
		{
			dword2[1] |= 0x0100;
			m_vm32->access_xdt( this, (ti==0), IA32_WRITE, index, dword2 );
		}
	}
	return;
}

void  IA32_CPU::parse_selector(Bit16u raw_selector, ia32_selector_t *selector)
{
	selector->value  = raw_selector;
	selector->index  = raw_selector >> 3;
	selector->ti     = (raw_selector >> 2) & 0x01;
	selector->rpl    = raw_selector & 0x03;
}

void  IA32_CPU::parse_descriptor(Bit32u dword1, Bit32u dword2, ia32_descriptor_t *temp)
{
	Bit8u AR_byte;

	AR_byte        = dword2 >> 8;
	temp->p        = (AR_byte >> 7) & 0x01;
	temp->dpl      = (AR_byte >> 5) & 0x03;
	temp->segment  = (AR_byte >> 4) & 0x01;
	temp->type     = (AR_byte & 0x0f);
	temp->valid    = 0; /* start out invalid */


	if (temp->segment) { /* data/code segment descriptors */
		temp->u.segment.executable = (AR_byte >> 3) & 0x01;
		temp->u.segment.c_ed       = (AR_byte >> 2) & 0x01;
		temp->u.segment.r_w        = (AR_byte >> 1) & 0x01;
		temp->u.segment.a          = (AR_byte >> 0) & 0x01;

		temp->u.segment.limit      = (dword1 & 0xffff);
		temp->u.segment.base       = (dword1 >> 16) | ((dword2 & 0xFF) << 16);

		temp->u.segment.limit        |= (dword2 & 0x000F0000);
		temp->u.segment.g            =  (dword2 & 0x00800000) > 0;
		temp->u.segment.d_b          =  (dword2 & 0x00400000) > 0;
		temp->u.segment.avl          =  (dword2 & 0x00100000) > 0;
		temp->u.segment.base         |= (dword2 & 0xFF000000);

		if (temp->u.segment.g) {
			if ( (temp->u.segment.executable==0) && (temp->u.segment.c_ed) )
				temp->u.segment.limit_scaled = (temp->u.segment.limit << 12);
			else
				temp->u.segment.limit_scaled = (temp->u.segment.limit << 12) | 0x0fff;
		}
		else
			temp->u.segment.limit_scaled = temp->u.segment.limit;

		temp->valid    = 1;
	}
	else 
	{ 
		switch ( temp->type ) 
		{
		case  0: // reserved
		case  8: // reserved
		case 10: // reserved
		case 13: // reserved
			temp->valid    = 0;
			break;
		case IA32_SYS_SEGMENT_AVAIL_286_TSS:
		case IA32_SYS_SEGMENT_BUSY_286_TSS:
			temp->u.tss286.base  = (dword1 >> 16) | ((dword2 & 0xff) << 16);
			temp->u.tss286.limit = (dword1 & 0xffff);
			temp->valid    = 1;
			break;
		case IA32_SYS_SEGMENT_LDT:
			temp->u.ldt.base = (dword1 >> 16) | ((dword2 & 0xFF) << 16);
			temp->u.ldt.base |= (dword2 & 0xff000000);
			temp->u.ldt.limit = (dword1 & 0xffff);
			temp->valid    = 1;
			break;
		case IA32_286_CALL_GATE:
		case IA32_286_INTERRUPT_GATE:
		case IA32_286_TRAP_GATE:
			/* word count only used for call gate */
			temp->u.gate286.word_count = dword2 & 0x1f;
			temp->u.gate286.dest_selector = dword1 >> 16;
			temp->u.gate286.dest_offset   = dword1 & 0xffff;
			temp->valid = 1;
			break;
		case IA32_TASK_GATE:
			temp->u.taskgate.tss_selector = dword1 >> 16;
			temp->valid = 1;
			break;
		case IA32_SYS_SEGMENT_AVAIL_386_TSS:
		case IA32_SYS_SEGMENT_BUSY_386_TSS:
			temp->u.tss386.base  = (dword1 >> 16) |
				((dword2 & 0xff) << 16) |
				(dword2 & 0xff000000);
			temp->u.tss386.limit = (dword1 & 0x0000ffff) |
				(dword2 & 0x000f0000);
			temp->u.tss386.g     = (dword2 & 0x00800000) > 0;
			temp->u.tss386.avl   = (dword2 & 0x00100000) > 0;
			if (temp->u.tss386.g)
				temp->u.tss386.limit_scaled = (temp->u.tss386.limit << 12) | 0x0fff;
			else
				temp->u.tss386.limit_scaled = temp->u.tss386.limit;
			temp->valid = 1;
			break;

		case IA32_386_CALL_GATE:
		case IA32_386_INTERRUPT_GATE:
		case IA32_386_TRAP_GATE:
			temp->u.gate386.dword_count   = dword2 & 0x1f;
			temp->u.gate386.dest_selector = dword1 >> 16;
			temp->u.gate386.dest_offset   = (dword2 & 0xffff0000) | (dword1 & 0x0000ffff);
			temp->valid = 1;
			break;
		default: 
			temp->valid = 0;
		}
	}
}

void  IA32_CPU::load_ldtr(ia32_selector_t *selector, ia32_descriptor_t *descriptor)
{
	if ( (selector->value & 0xfffc)==0 ) 
	{
		ldtr.selector = *selector;
		ldtr.cache.valid = 0;
		return;
	}

	ldtr.cache = *descriptor; /* whole structure copy */
	ldtr.selector = *selector;

	if ( ldtr.cache.u.ldt.limit < 7) 
	{
		// BX_PANIC(("load_ldtr(): ldtr.limit < 7"));
	}

	ldtr.cache.valid = 1;
}

void  IA32_CPU::load_cs(ia32_selector_t *selector, ia32_descriptor_t *descriptor, Bit8u cpl)
{
	sregs[IA32_SEG_REG_CS].selector     = *selector;
	sregs[IA32_SEG_REG_CS].cache        = *descriptor;

	sregs[IA32_SEG_REG_CS].selector.rpl = cpl;
	sregs[IA32_SEG_REG_CS].cache.valid = 1; /* ??? */
	sregs[IA32_SEG_REG_CS].selector.value =(0xfffc &  sregs[IA32_SEG_REG_CS].selector.value) | cpl;

	invalidate_prefetch_q();
}

void  IA32_CPU::load_ss(ia32_selector_t *selector, ia32_descriptor_t *descriptor, Bit8u cpl)
{
	sregs[IA32_SEG_REG_SS].selector = *selector;
	sregs[IA32_SEG_REG_SS].cache = *descriptor;
	sregs[IA32_SEG_REG_SS].selector.rpl = cpl;
}

void  IA32_CPU::fetch_raw_descriptor(ia32_selector_t *selector, Bit32u *dword1, Bit32u *dword2, Bit8u exception_no)
{
	Bit32u dwords[2] = {};
	m_vm32->access_xdt( this, (selector->ti==0), IA32_READ, selector->index, dwords );
	*dword1 = dwords[0];
	*dword1 = dwords[2];
}

bx_bool IA32_CPU::fetch_raw_descriptor2(ia32_selector_t *selector, Bit32u *dword1, Bit32u *dword2)
{
	Bit32u dwords[2] = {};
	m_vm32->access_xdt( this, (selector->ti==0), IA32_READ, selector->index, dwords );
	*dword1 = dwords[0];
	*dword1 = dwords[2];
	return 1;
}
